﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using MugenInjection.Exceptions;

namespace MugenInjection.Infrastructure
{
    /// <summary>
    /// A static helper class that includes various parameter checking routines.
    /// </summary>
    internal static class Validate
    {
        /// <summary>
        /// Throws <see cref="ArgumentNullException"/> if the given argument is null.
        /// </summary>
        /// <exception cref="ArgumentNullException"> if tested value if null.</exception>
        /// <param name="argumentValue">Argument value to test.</param>
        /// <param name="paramName">Name of the parameter being tested. </param>
        public static void ArgumentNotNull(object argumentValue, [InvokerParameterName] string paramName)
        {
            if (argumentValue == null)
                throw new ArgumentNullException(paramName);
        }

        /// <summary>
        /// Throws an exception if the tested string argument is null or the empty string.
        /// </summary>
        /// <exception cref="ArgumentNullException">Thrown if string value is null.</exception>
        /// <exception cref="ArgumentException">Thrown if the string is empty</exception>
        /// <param name="argumentValue">Argument value to check.</param>
        /// <param name="argumentName">Name of argument being checked.</param>
        public static void ArgumentNotNullOrEmpty(string argumentValue, [InvokerParameterName]
                                                  string argumentName)
        {
            if (argumentValue == null) throw new ArgumentNullException(argumentName);
            if (argumentValue.Length == 0) throw new ArgumentException("Argument cannot be empty.", argumentName);
        }

        internal static void NotSupportedIfNull(object argumentValue)
        {
            if (argumentValue == null)
                throw new NotSupportedException();
        }

        internal static Exception EnumOutOfRange(Enum eEnum, string argumentName)
        {
#if SILVERLIGHT
            return new ArgumentOutOfRangeException(argumentName, string.Format("Enum with value = {0}, out of range.", eEnum));
#else
            return new ArgumentOutOfRangeException(argumentName, eEnum, "Enum out of range.");
#endif

        }

        internal static void ArgumentNotNullAndNonZeroLength<T>(IList<T> argumentValue, [InvokerParameterName]
                                                  string argumentName)
        {
            ArgumentNotNull(argumentValue, argumentName);
            if (argumentValue.Count == 0)
                throw new ArgumentException(string.Format("The length of {0} cannot be 0.", argumentName), argumentName);
        }

        internal static void EnsureIsHaveCommonParent(IEnumerable<Type> services)
        {
            Type currentType = typeof(object);
            foreach (Type service in services)
            {
#if NETFX_CORE
                if (service.GetTypeInfo().IsInterface) continue;
                if (service.GetTypeInfo().IsSubclassOf(currentType))
#else
                if (service.IsInterface) continue;
                if (service.IsSubclassOf(currentType))
#endif
                {
                    currentType = service;
                    continue;
                }
                if (service.IsAssignableFrom(currentType)) continue;
                throw new NotSupportedException(
                    @"It is impossible for different classes create a common binding. 
The class must have a common parent, for example Alfa --> Bravo --> Omega, but not Alfa --> Bravo; Alfa --> Omega.");
            }
        }

        internal static void FactoryReturnNotNullValue(object argumentValue, string valueName)
        {
            if (argumentValue == null)
                throw new InvalidBindingException(string.Format("The factory for creating '{0}', returned a null value.", valueName));
        }

        internal static void EnsureIsInitializedValue(object value, string valueName)
        {
            if (value == null)
                throw new NullReferenceException(string.Format("The '{0}' is not initialized", valueName));
        }

        internal static void PropertyNotNull(object value, string propertyName)
        {
            if (value == null)
                throw new ArgumentNullException("value", string.Format("The property '{0}' cannot be null.", propertyName));
        }

        internal static void IsAssignable(Type typeFrom, object value)
        {
            if (value != null)
                IsAssignable(typeFrom, value.GetType());
        }

        internal static void IsAssignable(Type typeFrom, Type typeTo)
        {
            if (!typeFrom.IsAssignableFrom(typeTo))
                throw new ArgumentException(string.Format("The type of '{0}' is not assignable from '{1}'.", InjectorUtilsInternal.FormatType(typeFrom), InjectorUtilsInternal.FormatType(typeTo)));
        }

        internal static void IsAssignable<T>(Type typeTo)
        {
            IsAssignable(typeof(T), typeTo);
        }
    }
}